﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace CommonExtensions.ObjectExtensions.Reflection
{
    public static class Extensions
    {
        public static object GetPropertyValue(this object obj, string propertyName)
        {
            return obj.GetPropertyInfo(propertyName).GetValue(obj, null);
        }

        public static IEnumerable<T> GetPropertyAttributes<T>(this object obj, string propertyName)
        {
            return obj.GetPropertyInfo(propertyName).GetCustomAttributes(typeof(T), true).Cast<T>();
        }

        private static PropertyInfo GetPropertyInfo(this object obj, string propertyName)
        {
            if (obj.IsNull())
                throw new ArgumentNullException("obj");
            if (string.IsNullOrEmpty(propertyName))
                throw new ArgumentNullException("obj");

            PropertyInfo propertyInfo = obj.GetType().GetProperty(propertyName);

            if (propertyInfo.IsNull())
            {
                throw new InvalidOperationException(string.Format("Object {0} does not have public property named {1}",
                                                                  obj, propertyName));
            }

            return propertyInfo;
        }

        public static IEnumerable<MethodInfo> GetMethodsWithAttribute<T>(this object obj, Func<T, bool> methodFilterProc)
            where T : Attribute
        {
            if (obj.IsNull())
                throw new ArgumentNullException("obj");
            if (methodFilterProc.IsNull())
                throw new ArgumentNullException("methodFilterProc");

            return
                from methodInfo in obj.GetType().GetMethods(
                    BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Cast<MethodInfo>()
                let attributes = methodInfo.GetCustomAttributes(typeof(T), true).Cast<T>()
                where attributes.Count() > 0 && attributes.Any(methodFilterProc)
                select methodInfo;
        }
    }
}